﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using AutoAllocService.Map.Models;
using BestWise.Common;
using log4net;
using Newtonsoft.Json;
using RestEase;

namespace AutoAllocService.Map
{
    public class DriverService
    {
        private static readonly ILog _log = LogManager.GetLogger(typeof(DriverService));

        private const string searchUrl = "http://yingyan.baidu.com/api/v3/entity/boundsearch";

        private readonly string drivingUrl = "http://api.map.baidu.com/routematrix/v2/driving?output=json&ak=" +
                                             Program.AppSetting.Baidu.AK;

        private readonly string driverApiBaseUrl = Program.AppSetting.CallCenter.Url;

        public static async Task<EntityResult> GetEntities(double latitude, double longitude)
        {
            var p = Location.GetSqrPoints(latitude, longitude, Program.AppSetting.Baidu.Distance);

            var bounds =
                $"{Math.Round(p.Item1.Item1, 2)},{Math.Round(p.Item1.Item2, 2)};{Math.Round(p.Item2.Item1, 2)},{Math.Round(p.Item2.Item2, 2)}";
            var reqUrl = searchUrl +
                         $"?ak={Program.AppSetting.Baidu.AK}&service_id={Program.AppSetting.Baidu.ServiceId}&bounds={bounds}";
            try
            {
                var api = RestClient.For<IYingYanApi>("http://yingyan.baidu.com/api/v3");
                var result = await api.GetEntites(Program.AppSetting.Baidu.AK, Program.AppSetting.Baidu.ServiceId, bounds);

                var nowTick = DateTime.UtcNow.ToTicks() / 1000;
                var tick = nowTick - 600;
                var entities = result.Entities.Where(x => x.LatestLocation.LocTime >= tick);
                foreach (var driverEntity in entities)
                {
                    result.Entities.Remove(driverEntity);
                }

                return result;
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }

            return null;
        }

        public static async Task<List<DistanceEntity>> GetEntitiesOrdered(double latitude, double longitude, string bookingID = null)
        {
            var res = await GetEntities(latitude, longitude);
            if (res.Status != 0)
                return null;

            var origins = $"{Math.Round(latitude, 6)},{Math.Round(longitude, 6)}";
            var points = new List<string>();

            var entities = new List<DriverEntity>();

            if (res.Entities == null || !res.Entities.Any())
            {
                _log.Warn("there is no online drivers");
                return null;
            }

            if (!string.IsNullOrEmpty(bookingID))
            {
                var avaliableDrivers = await GetAvaliableDrivers(bookingID);

                if (avaliableDrivers == null || !avaliableDrivers.Any())
                {
                    _log.Warn("there is no online drivers");
                    return null;
                }
                entities.Clear();
                var avliableCodes = avaliableDrivers.Select(x => x.Code);

                foreach (var driverEntity in res.Entities)
                {
                    if (avliableCodes.Contains(driverEntity.EntityName))
                    {
                        entities.Add(driverEntity);
                    }
                }
            }
            else
            {
                entities = res.Entities;
            }

            foreach (var driverEntity in entities)
            {
                var loc = driverEntity.LatestLocation;
                points.Add($"{Math.Round(loc.Latitude, 6)},{Math.Round(loc.Longitude, 6)}");
                if (points.Count > 24)
                    break;
            }
            var destinations = string.Join('|', points);

            try
            {
                var client = RestClient.For<IMapApi>("http://api.map.baidu.com");
                var result = await client.GetDistance(origins, destinations, Program.AppSetting.Baidu.AK);

                for (int i = 0; i < result.Result.Count; i++)
                {
                    result.Result[i].Entity = entities[i].EntityName;
                }

                return result.Result.OrderBy(x => x.Duration.Value).ToList();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }

            return null;
        }

        public static async Task<List<DriverModel>> GetAvaliableDrivers(string bookingID)
        {
            try
            {
                var api = RestClient.For<ICallCenterApi>(Program.AppSetting.CallCenter.Url);
                var result = await api.GetAvaliableDrviers(bookingID);

                if (result.Status != 1)
                {
                    Console.WriteLine(result.Message);
                    return null;
                }

                return result.Data;
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
            return null;
        }

        public static async Task<List<DistanceEntity>> GetDrivers(double latitude, double longitude, string bookingID)
        {
            var drivers = new List<DistanceEntity>();
            try
            {
                var online_drivers = await GetEntitiesOrdered(latitude, longitude, bookingID);
                if (online_drivers == null || !online_drivers.Any())
                {
                    _log.Warn("there is no online drivers");
                    return null;
                }
                //var avaliable_drivers = await GetAvaliableDrivers(bookingID);
                //if (avaliable_drivers == null || !avaliable_drivers.Any())
                //{
                //    _log.Warn($"there is no avaliable drivers for {bookingID}");
                //    return null;
                //}
                //var avaliableCodes = avaliable_drivers.Select(x => x.Code);
                //var onlineCodes = online_drivers.Select(x => x.Entity);
                //foreach (var driver in avaliable_drivers)
                //{
                //    foreach (var onlineDriver in online_drivers)
                //    {
                //        if (onlineDriver.Entity == driver.Code)
                //        {
                //            drivers.Add(onlineDriver);
                //        }
                //    }
                //}
                drivers = online_drivers;

                drivers = drivers.OrderBy(x => x.Duration.Value).ToList();

            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                return null;
            }
            return drivers;
        }
    }
}
